﻿using CorpStore.Common;
using CorpStore.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.UI.StartScreen;

namespace CorpStore.ViewModels
{
    /// <summary>
    /// This is the view model for the AppDetailsPage 
    /// </summary>
    class AppDetailsViewModel : BaseViewModel
    {
        /// <summary>
        /// App currently being viewed on AppDetails page
        /// </summary>
        PackageInfo currentApp;
        public PackageInfo CurrentApp
        {
            get { return currentApp; }
            set { currentApp = value; OnPropertyChanged(); OnPropertyChanged("InstallRunText"); }
        }

        ScreenshotInfo selectedScreenshot;
        public ScreenshotInfo SelectedScreenshot
        {
            get { return selectedScreenshot; }
            set { selectedScreenshot = value; OnPropertyChanged(); }
        }

        /// <summary>
        /// Text displayed within Install/Install&Run button 
        /// </summary>
        public string InstallRunText 
        { 
            get 
            {
                if (CurrentApp != null && !string.IsNullOrEmpty(CurrentApp.InstalledLocation))
                    return "Run";
                else
                    return "Install & Run"; 
            }
        }


        /// <summary>
        /// Return description of pinned state of current app
        /// </summary>
        public string PinUnpinText
        {
            get
            {
                if (IsPinned)
                    return "Un-Pin";
                else
                    return "Pin";
            }
        }


        /// <summary>
        /// returns true if current app is pinned to the start screen
        /// </summary>
        public bool IsPinned { get { return CurrentApp != null && SecondaryTile.Exists(CurrentApp.FamilyName);  } }


        /// <summary>
        /// Saves the current state of this viewModel so that it can be restored after suspension/termination
        /// </summary>
        /// <param name="pageState"></param>
        public void SaveState(ref Dictionary<string, object> pageState)
        {
            if (currentApp != null)
                pageState["currentApp"] = JsonConvert.SerializeObject(CurrentApp);
            else
                pageState["currentApp"] = "";
        }


        /// <summary>
        /// This function should be called after the AppDetails page has been loaded
        /// </summary>
        /// <param name="serializedNavData"></param>
        /// <param name="pageState"></param>
        /// <returns></returns>
        public async Task<bool> InitOrRestoreStateAsync(string serializedNavData, Dictionary<string, object> pageState)
        {
            bool blnOK = false;

            if (pageState != null)
            {
                // we are being restored - handle it
                if (pageState.ContainsKey("currentApp"))
                {
                    CurrentApp = JsonConvert.DeserializeObject<PackageInfo>(pageState["currentApp"].ToString());
                    logInfo("Deserialized currentApp object OK");
                }
            }

            if (CurrentApp == null)
                // no current app was passed to us - we must look it up by family name 
                await initFromNavDataAsync(serializedNavData);

            OnPropertyChanged("PinUnpinText");

            return blnOK;
        }



        /// <summary>
        /// This method will attempt to restore the state of the AppDetails page based on 
        /// the serialized data contained in 'serializedData'.  It is assumed that this
        /// serialized data is actually a serialized version of NavData.
        /// </summary>
        /// <param name="serializedData"></param>
        /// <returns></returns>
        private async Task<bool> initFromNavDataAsync(string serializedData)
        {
            bool blnOK = false;
            string errStr;

            logInfo("Attempting to deserialize navData");
            NavData navData = NavData.Deserialize(serializedData, out errStr);
            if (navData != null)
            {
                // we were able to deserialize the data - check the contents of navData out
                logInfo("Deserialized navData OK; familyName = '{0}'", navData.FamilyName);
                if (navData.CurrentApp != null)
                {
                    // we were passed a currentApp already - just use it
                    CurrentApp = navData.CurrentApp;
                    blnOK = true;
                    logInfo("Set CurrentApp to navData.CurrentApp OK");
                }
                else
                {
                    PackageInfo info;
                    logInfo("Attempting to find app with a familyName of '{0}' in init", navData.FamilyName);
                    info = await findCurrentAppAsync(navData.FamilyName);
                    if (info != null)
                    {
                        CurrentApp = info;
                        blnOK = true;
                        logInfo("Found app with familyName '{0}' OK - setting CurrentApp equal to it", navData.FamilyName);
                    }
                }
            }
            else
                if (!string.IsNullOrEmpty(serializedData))
                    logErr("Error deserializing navData '{0}': {1}", serializedData, errStr);

            return blnOK;
        }


        private async Task<PackageInfo> findCurrentAppAsync(string familyName)
        {
            PackageInfo foundApp = null;

            // first load master catalog
            logInfo("Attempting to load all applications from MasterCatalog.json...");
            PackageInfoGroupList groupList = await PackageIO.LoadApplicationGroupsAsync();

            foundApp = groupList.Groups.SelectMany(g => g.Applications).FirstOrDefault(a => a.FamilyName.Equals(familyName, StringComparison.OrdinalIgnoreCase));

            if (groupList != null)
            {
                // loaded all groups ok - now search for app across all groups 
                foreach (PackageInfoGroup group in groupList.Groups)
                {
                    foreach (PackageInfo app in group.Applications)
                    {
                        if (app.FamilyName.Equals(familyName, StringComparison.OrdinalIgnoreCase))
                        {
                            foundApp = app;
                            break;
                        }
                    }
                    if (foundApp != null) break;
                }
            }
            else
                logErr("groupList was null from PackageIO.LoadApplicationGroups(); check contents of MasterCatalog.json");

            return foundApp;
        }


        public async Task LaunchAppAsync(PackageInfo app)
        {
            Uri uri = null;

            logInfo("At top of LaunchAppAsync()");
            if (app != null)
            {
                logInfo("Checking app type");
                switch (app.AppType())
                {
                    case eAppType.storeApp:
                        uri = new Uri(string.Format("corpstore://startStoreApp?familyName={0}&storeID={1}", app.FamilyName, app.StoreID));
                        break;

                    case eAppType.enterpriseApp:
                        uri = new Uri(string.Format("corpstore://startEnterpriseApp?familyName={0}&displayName=\"{1}\"&appxFn=\"{2}\"", app.FamilyName, app.DisplayName, app.AppxFn));
                        break;

                    case eAppType.win32App:
                        uri = new Uri(string.Format("corpstore://startWin32App?exeName=\"{0}\"&args=\"{1}\"", app.ExeName, app.Args));
                        break;
                }

                logInfo("Attempting to launch {0} type app '{1}'", app.AppType().ToString(), app.DisplayName);
                if (uri != null && !await Windows.System.Launcher.LaunchUriAsync(uri))
                {
                    logErrWithMsg("Error launching app '{0}'; make sure that CorpStoreServer has been registered with the 'corpStore' protocol");
                }
                else
                    logInfo("Launched '{0}' application '{1}' OK", app.AppType().ToString(), app.DisplayName);
            } // app not null
            else
                logErrWithMsg("Internal error launching app - app not defined");
        }


        public async Task<bool> PinUnpinCurrentAppAsync()
        {
            bool blnOK = false;

            if (IsPinned)
            {
                // unpin app
                // track was pinned already - ask user if ok to delete
                var tile = new SecondaryTile(CurrentApp.FamilyName);
                blnOK = await tile.RequestDeleteAsync();
                OnPropertyChanged("PinUnpinText");
            }
            else
            {
                // app is not pinned yet - pin it
                blnOK = await StoreHelper.GenLib.CreateSecondaryTileAsync(CurrentApp.FamilyName, CurrentApp.DisplayName,
                                                       new Uri(CurrentApp.LogoFn),
                                                       new Uri(CurrentApp.SplashScreenFn),
                                                       Windows.UI.Colors.Tan, true, CurrentApp.FamilyName + " launch");
                                                        
                OnPropertyChanged("PinUnpinText");
            }

            return blnOK;
        }


        private void logErr(string msg, params object[] p)
        {
            App.LogErr(msg, p);
        }

        private void logErrWithMsg(string msg, params object[] p)
        {
            App.LogErrWithMsg(msg, p);
        }

        private void logInfo(string msg, params object[] p)
        {
            App.LogInfo(msg, p);
        }

    }
}
